#pragma once
#include <thread>
#include <atomic>
#include <condition_variable>
namespace hhfox
{
// c++11只能通过sfine写法
#define HAS_REQUIRED_MEMBERS_HELPER(MemberName)                                                         \
    template <typename T, typename... Args>                                                             \
    struct HasRequiredMembersHelper_##MemberName                                                        \
    {                                                                                                   \
        template <typename U>                                                                           \
        static auto test(U *obj)                                                                        \
            -> decltype((void)std::declval<U>().MemberName(std::declval<Args>()...), std::true_type()); \
                                                                                                        \
        template <typename U>                                                                           \
        static auto test(...) -> decltype(std::false_type());                                           \
                                                                                                        \
        static constexpr bool value = std::is_same<decltype(test<T>(nullptr)), std::true_type>::value;  \
    };

#define HAS_REQUIRED_STATIC_MEMBERS_HELPER(MemberName)                                                  \
    template <typename T, typename... Args>                                                             \
    struct HasRequiredStaticMembersHelper_##MemberName                                                  \
    {                                                                                                   \
        template <typename U>                                                                           \
        static auto test() -> decltype((void)U::MemberName(std::declval<Args>()...), std::true_type()); \
                                                                                                        \
        template <typename U>                                                                           \
        static auto test(...) -> decltype(std::false_type());                                           \
                                                                                                        \
        static constexpr bool value = std::is_same<decltype(test<T>()), std::true_type>::value;         \
    };

    HAS_REQUIRED_MEMBERS_HELPER(quit);
    HAS_REQUIRED_MEMBERS_HELPER(run);

    template <typename T>
    class Callable
    {
    public:
        Callable() : stop_(true)
        {
            static_assert(HasRequiredMembersHelper_quit<T>::value, "T must implement quit()!");
            static_assert(HasRequiredMembersHelper_run<T>::value, "T must implement run()!");
        }

        virtual ~Callable()
        {
            if (isRunning())
                stop();
        }

        void start()
        {
            if (!isRunning())
            {
                std::lock_guard<std::mutex> lock(mutex_);
                norun_ = false;
                thread_ = std::thread(&Callable::prepareRun, this);
            }
        }

        void stop()
        {
            if (!isRunning())
            {
                return;
            }
            stop_ = true;
            norun_ = true;
            static_cast<T *>(this)->quit();
            {
                std::unique_lock<std::mutex> lock(mutex_);
                condition_.notify_all();
            }
            if (thread_.joinable())
            {
                thread_.join();
            }
        }

        bool isRunning() const { return thread_.joinable(); }

        bool needStop() const { return stop_.load() || norun_.load(); }

        template <typename Rep, typename Period>
        void sleep(const std::chrono::duration<Rep, Period> &duration)
        {
            std::unique_lock<std::mutex> lock(mutex_);
            condition_.wait_for(lock, duration, [this]()
                                { return stop_.load(); });
        }

    protected:
        void prepareRun()
        {
            stop_ = false;
            if (!norun_)
                static_cast<T *>(this)->run();
            stop_ = true;
        }

    private:
        std::thread thread_;
        std::atomic<bool> stop_;
        std::atomic<bool> norun_; // 解决当static_cast<T*>(this)->run()比stop后运行的时候，此时无法正确退出的问题
        mutable std::mutex mutex_;
        std::condition_variable condition_;
    };

} // namespace hhfox